// 版权归Go作者所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package escape

import (
	"fmt"
	"math"
	"strings"

	"cmd/compile/internal/base"
	"cmd/compile/internal/ir"
	"cmd/compile/internal/logopt"
	"cmd/compile/internal/typecheck"
	"cmd/compile/internal/types"
	"cmd/internal/src"
)

// 逃逸分析。
// None
// 这里我们分析函数以确定哪些Go变量
// （包括隐式分配，如调用“new”或“make”，
// 复合文本等）可以在堆栈上分配。两个
// 我们必须确保的关键不变量是：（1）指向堆栈对象的指针
// 无法存储在堆中，并且（2）指向堆栈对象的指针
// 无法超过该对象（例如，因为声明函数
// 返回并销毁了对象的堆栈帧，或其空间为
// 对逻辑上不同的变量跨循环迭代重用）。
// None
// 我们通过AST的静态数据流分析来实现这一点。
// 首先，我们构造一个有向加权图，其中顶点
// （称为“位置”）表示语句分配的变量
// 和表达式以及边表示变量之间的赋值
// （权重表示寻址/解引用计数）。
// None
// 下一步，我们在图中查找可能存在的赋值路径
// 违反上述不变量。如果变量v的地址为
// 存储在堆中或可能比它更长寿的其他地方，那么v是
// 标记为需要堆分配。
// None
// 为了支持过程间分析，我们还记录了来自
// 将每个函数的参数添加到堆及其结果
// 参数。此信息总结为“参数标签”，
// 用于静态呼叫站点，以改进对
// 函数参数。

// 构建位置图。
// None
// 每个分配语句（如变量声明）或
// 表达式（例如，“new”或“make”）首先映射到唯一的
// “位置。”
// None
// 我们还将每个Go分配建模为一条有向边
// 位置。解引用操作数减去
// 寻址操作记录为边的权重（称为
// “解除债务”）。例如：
// None
// p=&q
// p=q
// p=*q
// p=**q
// None
// p=***&q
// None
// 请注意，&运算符只能应用于可寻址
// 表达式，而表达式&x本身是不可寻址的，所以
// derefs不能低于-1。
// None
// 每个Go语言结构都被降低到这个表示中，
// 通常对流程、路径或上下文不敏感；和
// 在复合变量中没有区分元素。对于
// 例子：
// None
// var x结构{f，g*int}
// 变量u[]*int
// None
// x、 f=u[0]
// None
// 被简单地建模为
// None
// x=*u
// None
// 也就是说，我们不区分x.f和x.g，也不区分u[0]和u[1]，
// 然而，我们确实记录了所涉及的隐式解引用
// 在索引切片时。

// 批处理保存在整个批处理中共享的转义分析状态
// 一次分析的一批函数。
type batch struct {
	allLocs  []*location
	closures []closure

	heapLoc  location
	blankLoc location
}

// 闭包包含闭包表达式及其溢出孔（即。，
// 其中，代表存储到其闭合记录中的孔）。
type closure struct {
	k   hole
	clo *ir.ClosureExpr
}

// 转义保存特定于正在分析的单个函数的状态
// 一批之内。
type escape struct {
	*batch

	curfn *ir.Func // 正在分析的函数

	labels map[*types.Sym]labelState // 已知标签

	// loopDepth统计当前循环中的嵌套深度
	// curfn。它在每个“for”循环内和每个
	// 带有相应向后“goto”的标签（即。，
	// 非结构化循环）。
	loopDepth int
}

// 位置表示存储Go的抽象位置
// 变量
type location struct {
	n         ir.Node  // 表示变量或表达式（如有）
	curfn     *ir.Func // 封闭函数
	edges     []edge   // 传入边
	loopDepth int      // 申报时的深度

	// resultIndex记录的元组索引（从1开始）用于
	// PPARAMOUT变量在其函数的结果类型中。
	// 对于非PPARAMOUT变量，它是0。
	resultIndex int

	// 在walkOne中使用derefs和walkgen来跟踪
	// 从walk根的最小解引用。
	derefs  int // >= -1
	walkgen uint32

	// dst和dstEdgeindex跟踪下一个即时分配
	// 漫游期间的目标位置，以及索引
	// 指回此位置的边的。
	dst        *location
	dstEdgeIdx int

	// walkAll使用queued跟踪此位置是否为
	// 在步行队列中。
	queued bool

	// escapes报告所表示变量的地址是否为
	// 逃脱；也就是说，变量是否必须是heap
	// 分配。
	escapes bool

	// 瞬态报告所表示表达式的
	// 地址不在语句的有效期内；就是
	// 它的存储可以立即重复使用。
	transient bool

	// ParameterSC记录所表示参数的泄漏集。
	paramEsc leaks

	captured   bool // 闭包是否捕获了此变量？
	reassigned bool // 此变量是否已重新分配？
	addrtaken  bool // 是否已获取此变量的地址？
}

// 边表示两个Go变量之间的赋值边。
type edge struct {
	src    *location
	derefs int // >= -1
	notes  *note
}

// 从节点打印调用Fmt以打印有关逃逸分析结果的信息。
func Fmt(n ir.Node) string {
	text := ""
	switch n.Esc() {
	case ir.EscUnknown:
		break

	case ir.EscHeap:
		text = "esc(h)"

	case ir.EscNone:
		text = "esc(no)"

	case ir.EscNever:
		text = "esc(N)"

	default:
		text = fmt.Sprintf("esc(%d)", n.Esc())
	}

	if n.Op() == ir.ONAME {
		n := n.(*ir.Name)
		if loc, ok := n.Opt.(*location); ok && loc.loopDepth != 0 {
			if text != "" {
				text += " "
			}
			text += fmt.Sprintf("ld(%d)", loc.loopDepth)
		}
	}

	return text
}

// Batch对最小批次的数据执行转义分析
// 功能。
func Batch(fns []*ir.Func, recursive bool) {
	for _, fn := range fns {
		if fn.Op() != ir.ODCLFUNC {
			base.Fatalf("unexpected node: %v", fn)
		}
	}

	var b batch
	b.heapLoc.escapes = true

	// 从语法树构造数据流图。
	for _, fn := range fns {
		if base.Flag.W > 1 {
			s := fmt.Sprintf("\nbefore escape %v", fn)
			ir.Dump(s, fn)
		}
		b.initFunc(fn)
	}
	for _, fn := range fns {
		if !fn.IsHiddenClosure() {
			b.walkFunc(fn)
		}
	}

	// 我们走遍了函数体，所以我们到处都能看到
	// 变量可能已重新分配或已获取其地址。现在我们
	// 可以决定闭包是否应该捕获其自由变量
	// 通过价值或参考。
	for _, closure := range b.closures {
		b.flowClosure(closure.k, closure.clo)
	}
	b.closures = nil

	for _, loc := range b.allLocs {
		if why := HeapAllocReason(loc.n); why != "" {
			b.flow(b.heapHole().addr(loc.n, why), loc)
		}
	}

	b.walkAll()
	b.finish(fns)
}

func (b *batch) with(fn *ir.Func) *escape {
	return &escape{
		batch:     b,
		curfn:     fn,
		loopDepth: 1,
	}
}

func (b *batch) initFunc(fn *ir.Func) {
	e := b.with(fn)
	if fn.Esc() != escFuncUnknown {
		base.Fatalf("unexpected node: %v", fn)
	}
	fn.SetEsc(escFuncPlanned)
	if base.Flag.LowerM > 3 {
		ir.Dump("escAnalyze", fn)
	}

	// 为局部变量分配位置。
	for _, n := range fn.Dcl {
		if n.Op() == ir.ONAME {
			e.newLoc(n, false)
		}
	}

	// 初始化结果参数的resultIndex。
	for i, f := range fn.Type().Results().FieldSlice() {
		e.oldLoc(f.Nname.(*ir.Name)).resultIndex = 1 + i
	}
}

func (b *batch) walkFunc(fn *ir.Func) {
	e := b.with(fn)
	fn.SetEsc(escFuncStarted)

	// 标识标记非结构化循环头部的标签。
	ir.Visit(fn, func(n ir.Node) {
		switch n.Op() {
		case ir.OLABEL:
			n := n.(*ir.LabelStmt)
			if e.labels == nil {
				e.labels = make(map[*types.Sym]labelState)
			}
			e.labels[n.Label] = nonlooping

		case ir.OGOTO:
			// 如果我们在goto之前查看标签，
			// 这是一个循环标签。
			n := n.(*ir.BranchStmt)
			if e.labels[n.Label] == nonlooping {
				e.labels[n.Label] = looping
			}
		}
	})

	e.block(fn.Body)

	if len(e.labels) != 0 {
		base.FatalfAt(fn.Pos(), "leftover labels after walkFunc")
	}
}

func (b *batch) flowClosure(k hole, clo *ir.ClosureExpr) {
	for _, cv := range clo.Func.ClosureVars {
		n := cv.Canonical()
		loc := b.oldLoc(cv)
		if !loc.captured {
			base.FatalfAt(cv.Pos(), "closure variable never captured: %v", cv)
		}

		// 从不重新分配的<=128字节的变量按值捕获。
		n.SetByval(!loc.addrtaken && !loc.reassigned && n.Type().Size() <= 128)
		if !n.Byval() {
			n.SetAddrtaken(true)
		}

		if base.Flag.LowerM > 1 {
			how := "ref"
			if n.Byval() {
				how = "value"
			}
			base.WarnfAt(n.Pos(), "%v capturing by %s: %v (addr=%v assign=%v width=%d)", n.Curfn, how, n, loc.addrtaken, loc.reassigned, n.Type().Size())
		}

		// 将捕获的变量流到闭包。
		k := k
		if !cv.Byval() {
			k = k.addr(cv, "reference")
		}
		b.flow(k.note(cv, "captured by a closure"), loc)
	}
}

// 下面我们实现了在AST中漫游和录制的方法
// 数据流边缘。请注意，因为子表达式可能具有
// 副作用，始终访问整个AST是很重要的。
// None
// 例如，写下：
// None
// 如果x{
// e、 丢弃（左）
// }否则{
// e、 值（k，n.左）
// }
// None
// 或
// None
// 如果x{
// k=e.丢弃孔（）
// }
// e、 值（k，n.左）
// None
// 不要写：
// None
// None
// 如果x{
// e、 值（k，n.左）
// }

// stmt计算单个Go语句。
func (e *escape) stmt(n ir.Node) {
	if n == nil {
		return
	}

	lno := ir.SetPos(n)
	defer func() {
		base.Pos = lno
	}()

	if base.Flag.LowerM > 2 {
		fmt.Printf("%v:[%d] %v stmt: %v\n", base.FmtPos(base.Pos), e.loopDepth, e.curfn, n)
	}

	e.stmts(n.Init())

	switch n.Op() {
	default:
		base.Fatalf("unexpected stmt: %v", n)

	case ir.ODCLCONST, ir.ODCLTYPE, ir.OFALL, ir.OINLMARK:
		// 不

	case ir.OBREAK, ir.OCONTINUE, ir.OGOTO:
		// TODO（mdempsky）：处理死代码？

	case ir.OBLOCK:
		n := n.(*ir.BlockStmt)
		e.stmts(n.List)

	case ir.ODCL:
		// 记录声明处的循环深度。
		n := n.(*ir.Decl)
		if !ir.IsBlank(n.X) {
			e.dcl(n.X)
		}

	case ir.OLABEL:
		n := n.(*ir.LabelStmt)
		switch e.labels[n.Label] {
		case nonlooping:
			if base.Flag.LowerM > 2 {
				fmt.Printf("%v:%v non-looping label\n", base.FmtPos(base.Pos), n)
			}
		case looping:
			if base.Flag.LowerM > 2 {
				fmt.Printf("%v: %v looping label\n", base.FmtPos(base.Pos), n)
			}
			e.loopDepth++
		default:
			base.Fatalf("label missing tag")
		}
		delete(e.labels, n.Label)

	case ir.OIF:
		n := n.(*ir.IfStmt)
		e.discard(n.Cond)
		e.block(n.Body)
		e.block(n.Else)

	case ir.OFOR, ir.OFORUNTIL:
		n := n.(*ir.ForStmt)
		e.loopDepth++
		e.discard(n.Cond)
		e.stmt(n.Post)
		e.block(n.Body)
		e.loopDepth--

	case ir.ORANGE:
		// 对于键，值=范围X{Body}
		n := n.(*ir.RangeStmt)

		// X在循环外部进行计算。
		tmp := e.newLoc(nil, false)
		e.expr(tmp.asHole(), n.X)

		e.loopDepth++
		ks := e.addrs([]ir.Node{n.Key, n.Value})
		if n.X.Type().IsArray() {
			e.flow(ks[1].note(n, "range"), tmp)
		} else {
			e.flow(ks[1].deref(n, "range-deref"), tmp)
		}
		e.reassigned(ks, n)

		e.block(n.Body)
		e.loopDepth--

	case ir.OSWITCH:
		n := n.(*ir.SwitchStmt)

		if guard, ok := n.Tag.(*ir.TypeSwitchGuard); ok {
			var ks []hole
			if guard.Tag != nil {
				for _, cas := range n.Cases {
					cv := cas.Var
					k := e.dcl(cv) // 类型开关变量没有ODCL。
					if cv.Type().HasPointers() {
						ks = append(ks, k.dotType(cv.Type(), cas, "switch case"))
					}
				}
			}
			e.expr(e.teeHole(ks...), n.Tag.(*ir.TypeSwitchGuard).X)
		} else {
			e.discard(n.Tag)
		}

		for _, cas := range n.Cases {
			e.discards(cas.List)
			e.block(cas.Body)
		}

	case ir.OSELECT:
		n := n.(*ir.SelectStmt)
		for _, cas := range n.Cases {
			e.stmt(cas.Comm)
			e.block(cas.Body)
		}
	case ir.ORECV:
		// 托多（MDEMPSKY）：考虑E.丢弃（N.Lead）。
		n := n.(*ir.UnaryExpr)
		e.exprSkipInit(e.discardHole(), n) // 已经拜访过尼尼特了
	case ir.OSEND:
		n := n.(*ir.SendStmt)
		e.discard(n.Chan)
		e.assignHeap(n.Value, "send", n)

	case ir.OAS:
		n := n.(*ir.AssignStmt)
		e.assignList([]ir.Node{n.X}, []ir.Node{n.Y}, "assign", n)
	case ir.OASOP:
		n := n.(*ir.AssignOpStmt)
		// 托多（mdempsky）：担心奥尔什/奥什？
		e.assignList([]ir.Node{n.X}, []ir.Node{n.Y}, "assign", n)
	case ir.OAS2:
		n := n.(*ir.AssignListStmt)
		e.assignList(n.Lhs, n.Rhs, "assign-pair", n)

	case ir.OAS2DOTTYPE: // v、 ok=x（类型）
		n := n.(*ir.AssignListStmt)
		e.assignList(n.Lhs, n.Rhs, "assign-pair-dot-type", n)
	case ir.OAS2MAPR: // v、 ok=m[k]
		n := n.(*ir.AssignListStmt)
		e.assignList(n.Lhs, n.Rhs, "assign-pair-mapr", n)
	case ir.OAS2RECV, ir.OSELRECV2: // v、 ok=<-ch
		n := n.(*ir.AssignListStmt)
		e.assignList(n.Lhs, n.Rhs, "assign-pair-receive", n)

	case ir.OAS2FUNC:
		n := n.(*ir.AssignListStmt)
		e.stmts(n.Rhs[0].Init())
		ks := e.addrs(n.Lhs)
		e.call(ks, n.Rhs[0], nil)
		e.reassigned(ks, n)
	case ir.ORETURN:
		n := n.(*ir.ReturnStmt)
		results := e.curfn.Type().Results().FieldSlice()
		dsts := make([]ir.Node, len(results))
		for i, res := range results {
			dsts[i] = res.Nname.(*ir.Name)
		}
		e.assignList(dsts, n.Results, "return", n)
	case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER, ir.OCLOSE, ir.OCOPY, ir.ODELETE, ir.OPANIC, ir.OPRINT, ir.OPRINTN, ir.ORECOVER:
		e.call(nil, n, nil)
	case ir.OGO, ir.ODEFER:
		n := n.(*ir.GoDeferStmt)
		e.stmts(n.Call.Init())
		e.call(nil, n.Call, n)

	case ir.OTAILCALL:
		// TODO（mdempsky）：像正常通话一样对待吗？esc.go过去常常忽略它。
	}
}

func (e *escape) stmts(l ir.Nodes) {
	for _, n := range l {
		e.stmt(n)
	}
}

// 块类似于stmts，但保留loopDepth。
func (e *escape) block(l ir.Nodes) {
	old := e.loopDepth
	e.stmts(l)
	e.loopDepth = old
}

// expr模型计算表达式n并将结果流入
// 洞k。
func (e *escape) expr(k hole, n ir.Node) {
	if n == nil {
		return
	}
	e.stmts(n.Init())
	e.exprSkipInit(k, n)
}

func (e *escape) exprSkipInit(k hole, n ir.Node) {
	if n == nil {
		return
	}

	lno := ir.SetPos(n)
	defer func() {
		base.Pos = lno
	}()

	uintptrEscapesHack := k.uintptrEscapesHack
	k.uintptrEscapesHack = false

	if uintptrEscapesHack && n.Op() == ir.OCONVNOP && n.(*ir.ConvExpr).X.Type().IsUnsafePtr() {
		// 不
	} else if k.derefs >= 0 && !n.Type().HasPointers() {
		k.dst = &e.blankLoc
	}

	switch n.Op() {
	default:
		base.Fatalf("unexpected expr: %s %v", n.Op().String(), n)

	case ir.OLITERAL, ir.ONIL, ir.OGETG, ir.OTYPE, ir.OMETHEXPR, ir.OLINKSYMOFFSET:
		// 不

	case ir.ONAME:
		n := n.(*ir.Name)
		if n.Class == ir.PFUNC || n.Class == ir.PEXTERN {
			return
		}
		if n.IsClosureVar() && n.Defn == nil {
			return // 方法值包装中的“.this”
		}
		e.flow(k, e.oldLoc(n))

	case ir.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT:
		n := n.(*ir.UnaryExpr)
		e.discard(n.X)
	case ir.OADD, ir.OSUB, ir.OOR, ir.OXOR, ir.OMUL, ir.ODIV, ir.OMOD, ir.OLSH, ir.ORSH, ir.OAND, ir.OANDNOT, ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE:
		n := n.(*ir.BinaryExpr)
		e.discard(n.X)
		e.discard(n.Y)
	case ir.OANDAND, ir.OOROR:
		n := n.(*ir.LogicalExpr)
		e.discard(n.X)
		e.discard(n.Y)
	case ir.OADDR:
		n := n.(*ir.AddrExpr)
		e.expr(k.addr(n, "address-of"), n.X) // “地址”
	case ir.ODEREF:
		n := n.(*ir.StarExpr)
		e.expr(k.deref(n, "indirection"), n.X) // “间接”
	case ir.ODOT, ir.ODOTMETH, ir.ODOTINTER:
		n := n.(*ir.SelectorExpr)
		e.expr(k.note(n, "dot"), n.X)
	case ir.ODOTPTR:
		n := n.(*ir.SelectorExpr)
		e.expr(k.deref(n, "dot of pointer"), n.X) // “指针点”
	case ir.ODOTTYPE, ir.ODOTTYPE2:
		n := n.(*ir.TypeAssertExpr)
		e.expr(k.dotType(n.Type(), n, "dot"), n.X)
	case ir.OINDEX:
		n := n.(*ir.IndexExpr)
		if n.X.Type().IsArray() {
			e.expr(k.note(n, "fixed-array-index-of"), n.X)
		} else {
			// TODO（mdempsky）：修复原因文本。
			e.expr(k.deref(n, "dot of pointer"), n.X)
		}
		e.discard(n.Index)
	case ir.OINDEXMAP:
		n := n.(*ir.IndexExpr)
		e.discard(n.X)
		e.discard(n.Index)
	case ir.OSLICE, ir.OSLICEARR, ir.OSLICE3, ir.OSLICE3ARR, ir.OSLICESTR:
		n := n.(*ir.SliceExpr)
		e.expr(k.note(n, "slice"), n.X)
		e.discard(n.Low)
		e.discard(n.High)
		e.discard(n.Max)

	case ir.OCONV, ir.OCONVNOP:
		n := n.(*ir.ConvExpr)
		if ir.ShouldCheckPtr(e.curfn, 2) && n.Type().IsUnsafePtr() && n.X.Type().IsPtr() {
			// 启用-d=checkptr=2时，处理
			// 转换为不安全的。指针作为
			// 逃跑行动。这样可以更好地
			// 运行时插装，因为我们可以
			// 轻松检测堆上的对象边界
			// 而不是一堆。
			e.assignHeap(n.X, "conversion to unsafe.Pointer", n)
		} else if n.Type().IsUnsafePtr() && n.X.Type().IsUintptr() {
			e.unsafeValue(k, n.X)
		} else {
			e.expr(k, n.X)
		}
	case ir.OCONVIFACE:
		n := n.(*ir.ConvExpr)
		if !n.X.Type().IsInterface() && !types.IsDirectIface(n.X.Type()) {
			k = e.spill(k, n)
		}
		e.expr(k.note(n, "interface-converted"), n.X)
	case ir.OSLICE2ARRPTR:
		// 切片指针直接流向结果
		n := n.(*ir.ConvExpr)
		e.expr(k, n.X)
	case ir.ORECV:
		n := n.(*ir.UnaryExpr)
		e.discard(n.X)

	case ir.OCALLMETH, ir.OCALLFUNC, ir.OCALLINTER, ir.OLEN, ir.OCAP, ir.OCOMPLEX, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.OCOPY, ir.OUNSAFEADD, ir.OUNSAFESLICE:
		e.call([]hole{k}, n, nil)

	case ir.ONEW:
		n := n.(*ir.UnaryExpr)
		e.spill(k, n)

	case ir.OMAKESLICE:
		n := n.(*ir.MakeExpr)
		e.spill(k, n)
		e.discard(n.Len)
		e.discard(n.Cap)
	case ir.OMAKECHAN:
		n := n.(*ir.MakeExpr)
		e.discard(n.Len)
	case ir.OMAKEMAP:
		n := n.(*ir.MakeExpr)
		e.spill(k, n)
		e.discard(n.Len)

	case ir.ORECOVER:
		// 不

	case ir.OCALLPART:
		// 将receiver参数流到闭包和
		// 到接收器参数。

		n := n.(*ir.SelectorExpr)
		closureK := e.spill(k, n)

		m := n.Selection

		// 我们不知道如何调用方法值
		// 后来，所以保守地假设结果
		// 所有参数都流向堆。
		// None
		// TODO（mdempsky）：将ks更改为回调，以便
		// 我们不必创建这个切片？
		var ks []hole
		for i := m.Type.NumResults(); i > 0; i-- {
			ks = append(ks, e.heapHole())
		}
		name, _ := m.Nname.(*ir.Name)
		paramK := e.tagHole(ks, name, m.Type.Recv())

		e.expr(e.teeHole(paramK, closureK), n.X)

	case ir.OPTRLIT:
		n := n.(*ir.AddrExpr)
		e.expr(e.spill(k, n), n.X)

	case ir.OARRAYLIT:
		n := n.(*ir.CompLitExpr)
		for _, elt := range n.List {
			if elt.Op() == ir.OKEY {
				elt = elt.(*ir.KeyExpr).Value
			}
			e.expr(k.note(n, "array literal element"), elt)
		}

	case ir.OSLICELIT:
		n := n.(*ir.CompLitExpr)
		k = e.spill(k, n)
		k.uintptrEscapesHack = uintptrEscapesHack // 对于…uintptr参数

		for _, elt := range n.List {
			if elt.Op() == ir.OKEY {
				elt = elt.(*ir.KeyExpr).Value
			}
			e.expr(k.note(n, "slice-literal-element"), elt)
		}

	case ir.OSTRUCTLIT:
		n := n.(*ir.CompLitExpr)
		for _, elt := range n.List {
			e.expr(k.note(n, "struct literal element"), elt.(*ir.StructKeyExpr).Value)
		}

	case ir.OMAPLIT:
		n := n.(*ir.CompLitExpr)
		e.spill(k, n)

		// 映射键和值始终存储在堆中。
		for _, elt := range n.List {
			elt := elt.(*ir.KeyExpr)
			e.assignHeap(elt.Key, "map literal key", n)
			e.assignHeap(elt.Value, "map literal value", n)
		}

	case ir.OCLOSURE:
		n := n.(*ir.ClosureExpr)
		k = e.spill(k, n)
		e.closures = append(e.closures, closure{k, n})

		if fn := n.Func; fn.IsHiddenClosure() {
			for _, cv := range fn.ClosureVars {
				if loc := e.oldLoc(cv); !loc.captured {
					loc.captured = true

					// 忽略直线代码中变量的重新分配
					// 在第一次捕获之前使用闭包。
					if loc.loopDepth == e.loopDepth {
						loc.reassigned = false
					}
				}
			}

			for _, n := range fn.Dcl {
				// 添加的局部变量的位置
				// 关闭，如果需要，以防我们不包括
				// 批处理中用于转义的闭包func
				// 分析（发生在称为
				// 来自reflectdata.methodWrapper）
				if n.Op() == ir.ONAME && n.Opt == nil {
					e.with(fn).newLoc(n, false)
				}
			}
			e.walkFunc(fn)
		}

	case ir.ORUNES2STR, ir.OBYTES2STR, ir.OSTR2RUNES, ir.OSTR2BYTES, ir.ORUNESTR:
		n := n.(*ir.ConvExpr)
		e.spill(k, n)
		e.discard(n.X)

	case ir.OADDSTR:
		n := n.(*ir.AddStringExpr)
		e.spill(k, n)

		// OADDSTR的论点永远无法逃脱；
		// runtime.concatstrings确保了这一点。
		e.discards(n.List)
	}
}

// unsafeValue计算uintptr类型的算术表达式
// 用于从不安全的.Pointer进行转换。
func (e *escape) unsafeValue(k hole, n ir.Node) {
	if n.Type().Kind() != types.TUINTPTR {
		base.Fatalf("unexpected type %v for %v", n.Type(), n)
	}
	if k.addrtaken {
		base.Fatalf("unexpected addrtaken")
	}

	e.stmts(n.Init())

	switch n.Op() {
	case ir.OCONV, ir.OCONVNOP:
		n := n.(*ir.ConvExpr)
		if n.X.Type().IsUnsafePtr() {
			e.expr(k, n.X)
		} else {
			e.discard(n.X)
		}
	case ir.ODOTPTR:
		n := n.(*ir.SelectorExpr)
		if ir.IsReflectHeaderDataField(n) {
			e.expr(k.deref(n, "reflect.Header.Data"), n.X)
		} else {
			e.discard(n.X)
		}
	case ir.OPLUS, ir.ONEG, ir.OBITNOT:
		n := n.(*ir.UnaryExpr)
		e.unsafeValue(k, n.X)
	case ir.OADD, ir.OSUB, ir.OOR, ir.OXOR, ir.OMUL, ir.ODIV, ir.OMOD, ir.OAND, ir.OANDNOT:
		n := n.(*ir.BinaryExpr)
		e.unsafeValue(k, n.X)
		e.unsafeValue(k, n.Y)
	case ir.OLSH, ir.ORSH:
		n := n.(*ir.BinaryExpr)
		e.unsafeValue(k, n.X)
		// RHS不需要uintptr类型（#32959），也不能有意义
		// 无论如何，流指针。
		e.discard(n.Y)
	default:
		e.exprSkipInit(e.discardHole(), n)
	}
}

// discard计算表达式n的副作用，但放弃
// 它的价值。
func (e *escape) discard(n ir.Node) {
	e.expr(e.discardHole(), n)
}

func (e *escape) discards(l ir.Nodes) {
	for _, n := range l {
		e.discard(n)
	}
}

// addr计算可寻址表达式n并返回一个洞
// 表示存储到所表示的位置。
func (e *escape) addr(n ir.Node) hole {
	if n == nil || ir.IsBlank(n) {
		// 可能发生在特定情况下，范围内，可能其他情况下。
		return e.discardHole()
	}

	k := e.heapHole()

	switch n.Op() {
	default:
		base.Fatalf("unexpected addr: %v", n)
	case ir.ONAME:
		n := n.(*ir.Name)
		if n.Class == ir.PEXTERN {
			break
		}
		k = e.oldLoc(n).asHole()
	case ir.OLINKSYMOFFSET:
		break
	case ir.ODOT:
		n := n.(*ir.SelectorExpr)
		k = e.addr(n.X)
	case ir.OINDEX:
		n := n.(*ir.IndexExpr)
		e.discard(n.Index)
		if n.X.Type().IsArray() {
			k = e.addr(n.X)
		} else {
			e.discard(n.X)
		}
	case ir.ODEREF, ir.ODOTPTR:
		e.discard(n)
	case ir.OINDEXMAP:
		n := n.(*ir.IndexExpr)
		e.discard(n.X)
		e.assignHeap(n.Index, "key of map put", n)
	}

	return k
}

func (e *escape) addrs(l ir.Nodes) []hole {
	var ks []hole
	for _, n := range l {
		ks = append(ks, e.addr(n))
	}
	return ks
}

// 重新指定将与给定孔关联的位置标记为
// 重新分配，除非该位置表示声明的变量和
// 由where精确分配一次。
func (e *escape) reassigned(ks []hole, where ir.Node) {
	if as, ok := where.(*ir.AssignStmt); ok && as.Op() == ir.OAS && as.Y == nil {
		if dst, ok := as.X.(*ir.Name); ok && dst.Op() == ir.ONAME && dst.Defn == nil {
			// 声明的变量的零值赋值没有
			// 显式初始值。假设这是它的初始化
			// 陈述
			return
		}
	}

	for _, k := range ks {
		loc := k.dst
		// range语句声明的变量在每次迭代中都被赋值。
		if n, ok := loc.n.(*ir.Name); ok && n.Defn == where && where.Op() != ir.ORANGE {
			continue
		}
		loc.reassigned = true
	}
}

// assignList评估分配DST…=srcs。。。。
func (e *escape) assignList(dsts, srcs []ir.Node, why string, where ir.Node) {
	ks := e.addrs(dsts)
	for i, k := range ks {
		var src ir.Node
		if i < len(srcs) {
			src = srcs[i]
		}

		if dst := dsts[i]; dst != nil {
			// 在以下情况下检测uintptr到不安全指针的隐式转换：
			// 存储到reflect.{Slice，String}头中。
			if dst.Op() == ir.ODOTPTR && ir.IsReflectHeaderDataField(dst) {
				e.unsafeValue(e.heapHole().note(where, why), src)
				continue
			}

			// 筛选出一些无操作分配以进行转义分析。
			if src != nil && isSelfAssign(dst, src) {
				if base.Flag.LowerM != 0 {
					base.WarnfAt(where.Pos(), "%v ignoring self-assignment in %v", e.curfn, where)
				}
				k = e.discardHole()
			}
		}

		e.expr(k.note(where, why), src)
	}

	e.reassigned(ks, where)
}

func (e *escape) assignHeap(src ir.Node, why string, where ir.Node) {
	e.expr(e.heapHole().note(where, why), src)
}

// call计算调用表达式，包括内置调用。ks
// 应该包含表示函数被调用方
// 成果流动；其中是调用的OGO/ODEFER上下文（如果有）。
func (e *escape) call(ks []hole, call, where ir.Node) {
	topLevelDefer := where != nil && where.Op() == ir.ODEFER && e.loopDepth == 1
	if topLevelDefer {
		// 强制延迟记录的堆栈分配，除非
		// 使用开放编码延期（参见ssa.go）
		where.SetEsc(ir.EscNever)
	}

	argument := func(k hole, arg ir.Node) {
		if topLevelDefer {
			// 顶级延迟参数不会转义到
			// 堆，但它们确实需要持续到
			// 作用
			k = e.later(k)
		} else if where != nil {
			k = e.heapHole()
		}

		e.expr(k.note(call, "call parameter"), arg)
	}

	switch call.Op() {
	default:
		ir.Dump("esc", call)
		base.Fatalf("unexpected call op: %v", call.Op())

	case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER:
		call := call.(*ir.CallExpr)
		typecheck.FixVariadicCall(call)

		// 如果静态已知，则选择被调用函数。
		var fn *ir.Name
		switch call.Op() {
		case ir.OCALLFUNC:
			switch v := ir.StaticValue(call.X); {
			case v.Op() == ir.ONAME && v.(*ir.Name).Class == ir.PFUNC:
				fn = v.(*ir.Name)
			case v.Op() == ir.OCLOSURE:
				fn = v.(*ir.ClosureExpr).Func.Nname
			}
		case ir.OCALLMETH:
			fn = ir.MethodExprName(call.X)
		}

		fntype := call.X.Type()
		if fn != nil {
			fntype = fn.Type()
		}

		if ks != nil && fn != nil && e.inMutualBatch(fn) {
			for i, result := range fn.Type().Results().FieldSlice() {
				e.expr(ks[i], ir.AsNode(result.Nname))
			}
		}

		if r := fntype.Recv(); r != nil {
			argument(e.tagHole(ks, fn, r), call.X.(*ir.SelectorExpr).X)
		} else {
			// 对被调用函数表达式求值。
			argument(e.discardHole(), call.X)
		}

		args := call.Args
		for i, param := range fntype.Params().FieldSlice() {
			argument(e.tagHole(ks, fn, param), args[i])
		}

	case ir.OAPPEND:
		call := call.(*ir.CallExpr)
		args := call.Args

		// 如果满足以下条件，则附录切片可以直接流向结果
		// 它有足够的容量。或者，创建一个新堆
		// 可以分配slice，并且所有slice元素
		// 可能会流到堆中。
		appendeeK := ks[0]
		if args[0].Type().Elem().HasPointers() {
			appendeeK = e.teeHole(appendeeK, e.heapHole().deref(call, "appendee slice"))
		}
		argument(appendeeK, args[0])

		if call.IsDDD {
			appendedK := e.discardHole()
			if args[1].Type().IsSlice() && args[1].Type().Elem().HasPointers() {
				appendedK = e.heapHole().deref(call, "appended slice...")
			}
			argument(appendedK, args[1])
		} else {
			for _, arg := range args[1:] {
				argument(e.heapHole(), arg)
			}
		}

	case ir.OCOPY:
		call := call.(*ir.BinaryExpr)
		argument(e.discardHole(), call.X)

		copiedK := e.discardHole()
		if call.Y.Type().IsSlice() && call.Y.Type().Elem().HasPointers() {
			copiedK = e.heapHole().deref(call, "copied slice")
		}
		argument(copiedK, call.Y)

	case ir.OPANIC:
		call := call.(*ir.UnaryExpr)
		argument(e.heapHole(), call.X)

	case ir.OCOMPLEX:
		call := call.(*ir.BinaryExpr)
		argument(e.discardHole(), call.X)
		argument(e.discardHole(), call.Y)
	case ir.ODELETE, ir.OPRINT, ir.OPRINTN, ir.ORECOVER:
		call := call.(*ir.CallExpr)
		for _, arg := range call.Args {
			argument(e.discardHole(), arg)
		}
	case ir.OLEN, ir.OCAP, ir.OREAL, ir.OIMAG, ir.OCLOSE:
		call := call.(*ir.UnaryExpr)
		argument(e.discardHole(), call.X)

	case ir.OUNSAFEADD, ir.OUNSAFESLICE:
		call := call.(*ir.BinaryExpr)
		argument(ks[0], call.X)
		argument(e.discardHole(), call.Y)
	}
}

// tagHole返回用于计算传递给param的参数的孔。
// ks应包含表示函数位置的孔
// 被调用方的结果流。fn是静态已知的被调用函数，
// 如果有的话。
func (e *escape) tagHole(ks []hole, fn *ir.Name, param *types.Field) hole {
	// 如果这是一个动态调用，我们不能依赖param.Note。
	if fn == nil {
		return e.heapHole()
	}

	if e.inMutualBatch(fn) {
		return e.addr(ir.AsNode(param.Nname))
	}

	// 调用以前标记的函数。

	if param.Note == UintptrEscapesNote {
		k := e.heapHole()
		k.uintptrEscapesHack = true
		return k
	}

	var tagKs []hole

	esc := parseLeaks(param.Note)
	if x := esc.Heap(); x >= 0 {
		tagKs = append(tagKs, e.heapHole().shift(x))
	}

	if ks != nil {
		for i := 0; i < numEscResults; i++ {
			if x := esc.Result(i); x >= 0 {
				tagKs = append(tagKs, ks[i].shift(x))
			}
		}
	}

	return e.teeHole(tagKs...)
}

// inMutualBatch报告函数fn是否在
// 正在分析的相互递归函数。如果这是真的，
// fn尚未分析，因此其参数和结果
// 应直接合并到流程图中，而不是
// 依靠它的逃逸分析标签。
func (e *escape) inMutualBatch(fn *ir.Name) bool {
	if fn.Defn != nil && fn.Defn.Esc() < escFuncTagged {
		if fn.Defn.Esc() == escFuncUnknown {
			base.Fatalf("graph inconsistency: %v", fn)
		}
		return true
	}
	return false
}

// 一个孔表示一个Go评估的上下文
// 表示例如，当在“x=**p”中计算p时，我们会有一个洞
// dst==x，derefs==2。
type hole struct {
	dst    *location
	derefs int // >= -1
	notes  *note

	// addrtaken指示此上下文是否采用
	// 表达式，与地址是否实际
	// 被存储到一个变量中。
	addrtaken bool

	// UIntTrescapeShack表示此上下文正在评估
	// 辩论
	uintptrEscapesHack bool
}

type note struct {
	next  *note
	where ir.Node
	why   string
}

func (k hole) note(where ir.Node, why string) hole {
	if where == nil || why == "" {
		base.Fatalf("note: missing where/why")
	}
	if base.Flag.LowerM >= 2 || logopt.Enabled() {
		k.notes = &note{
			next:  k.notes,
			where: where,
			why:   why,
		}
	}
	return k
}

func (k hole) shift(delta int) hole {
	k.derefs += delta
	if k.derefs < -1 {
		base.Fatalf("derefs underflow: %v", k.derefs)
	}
	k.addrtaken = delta < 0
	return k
}

func (k hole) deref(where ir.Node, why string) hole { return k.shift(1).note(where, why) }
func (k hole) addr(where ir.Node, why string) hole  { return k.shift(-1).note(where, why) }

func (k hole) dotType(t *types.Type, where ir.Node, why string) hole {
	if !t.IsInterface() && !types.IsDirectIface(t) {
		k = k.shift(1)
	}
	return k.note(where, why)
}

// teeHole返回一个新孔，该孔流入ks的每个孔，
// 类似于Unix tee（1）命令。
func (e *escape) teeHole(ks ...hole) hole {
	if len(ks) == 0 {
		return e.discardHole()
	}
	if len(ks) == 1 {
		return ks[0]
	}
	// TODO（mdempsky）：如果只有一个非废弃孔，则进行优化？

	// 给定孔“l1=”、“l2=*”、“l3=*”、…，创建一个
	// 新的临时位置ltmp，将其连接到位，然后返回
	// “ltmp=”的孔。
	loc := e.newLoc(nil, true)
	for _, k := range ks {
		// 注意，“p=&q”和“p=&tmp；tmp=q”不是
		// 语义等价。组合像“l1”这样的孔
		// 和“l2=&”，我们需要将它们连接为“l1”=
		// *“ltmp”和“l2=ltmp”并返回“ltmp=&”
		// 相反
		if k.derefs < 0 {
			base.Fatalf("teeHole: negative derefs")
		}

		e.flow(k, loc)
	}
	return loc.asHole()
}

func (e *escape) dcl(n *ir.Name) hole {
	if n.Curfn != e.curfn || n.IsClosureVar() {
		base.Fatalf("bad declaration of %v", n)
	}
	loc := e.oldLoc(n)
	loc.loopDepth = e.loopDepth
	return loc.asHole()
}

// 溢出分配一个与表达式n，flows关联的新位置
// 它的地址为k，并返回一个向其传递值的孔。它是
// 适用于大多数分配存储的表达式。
func (e *escape) spill(k hole, n ir.Node) hole {
	loc := e.newLoc(n, true)
	e.flow(k.addr(n, "spill"), loc)
	return loc.asHole()
}

// later返回一个流入k的新孔，但要过一段时间。
// 其主要作用是防止临时材料的立即重复使用
// 订单期间引入的变量。
func (e *escape) later(k hole) hole {
	loc := e.newLoc(nil, false)
	e.flow(k, loc)
	return loc.asHole()
}

func (e *escape) newLoc(n ir.Node, transient bool) *location {
	if e.curfn == nil {
		base.Fatalf("e.curfn isn't set")
	}
	if n != nil && n.Type() != nil && n.Type().NotInHeap() {
		base.ErrorfAt(n.Pos(), "%v is incomplete (or unallocatable); stack allocation disallowed", n.Type())
	}

	if n != nil && n.Op() == ir.ONAME {
		n = n.(*ir.Name).Canonical()
	}
	loc := &location{
		n:         n,
		curfn:     e.curfn,
		loopDepth: e.loopDepth,
		transient: transient,
	}
	e.allLocs = append(e.allLocs, loc)
	if n != nil {
		if n.Op() == ir.ONAME {
			n := n.(*ir.Name)
			if n.Curfn != e.curfn {
				base.Fatalf("curfn mismatch: %v != %v for %v", n.Curfn, e.curfn, n)
			}

			if n.Opt != nil {
				base.Fatalf("%v already has a location", n)
			}
			n.Opt = loc
		}
	}
	return loc
}

func (b *batch) oldLoc(n *ir.Name) *location {
	if n.Canonical().Opt == nil {
		base.Fatalf("%v has no location", n)
	}
	return n.Canonical().Opt.(*location)
}

func (l *location) asHole() hole {
	return hole{dst: l}
}

func (b *batch) flow(k hole, src *location) {
	if k.addrtaken {
		src.addrtaken = true
	}

	dst := k.dst
	if dst == &b.blankLoc {
		return
	}
	if dst == src && k.derefs >= 0 { // dst=dst，dst=*dst。。。
		return
	}
	if dst.escapes && k.derefs < 0 { // dst=&src
		if base.Flag.LowerM >= 2 || logopt.Enabled() {
			pos := base.FmtPos(src.n.Pos())
			if base.Flag.LowerM >= 2 {
				fmt.Printf("%s: %v escapes to heap:\n", pos, src.n)
			}
			explanation := b.explainFlow(pos, dst, src, k.derefs, k.notes, []*logopt.LoggedOpt{})
			if logopt.Enabled() {
				var e_curfn *ir.Func // TODO（mdempsky）：修复。
				logopt.LogOpt(src.n.Pos(), "escapes", "escape", ir.FuncName(e_curfn), fmt.Sprintf("%v escapes to heap", src.n), explanation)
			}

		}
		src.escapes = true
		return
	}

	// TODO（mdempsky）：重复数据消除边缘？
	dst.edges = append(dst.edges, edge{src: src, derefs: k.derefs, notes: k.notes})
}

func (b *batch) heapHole() hole    { return b.heapLoc.asHole() }
func (b *batch) discardHole() hole { return b.blankLoc.asHole() }

// walkAll计算所有数据对之间的最小解引用
// 位置。
func (b *batch) walkAll() {
	// 我们使用一个工作队列来跟踪我们需要的位置
	// 去参观，反复走，直到我们到达一个固定点。
	// None
	// 我们从每个位置（包括堆）行走一次
	// 然后将每个位置从其转换重新排队
	// 瞬态->！转瞬即逝！转义->转义，每个转义都可以
	// 最多发生一次。所以我们去散步。

	// 后进先出队列，有足够的空间容纳e.allloc和e.heapLoc。
	todo := make([]*location, 0, len(b.allLocs)+1)
	enqueue := func(loc *location) {
		if !loc.queued {
			todo = append(todo, loc)
			loc.queued = true
		}
	}

	for _, loc := range b.allLocs {
		enqueue(loc)
	}
	enqueue(&b.heapLoc)

	var walkgen uint32
	for len(todo) > 0 {
		root := todo[len(todo)-1]
		todo = todo[:len(todo)-1]
		root.queued = false

		walkgen++
		b.walkOne(root, walkgen, enqueue)
	}
}

// walkOne计算从根到根的最小解引用次数
// 所有其他地点。
func (b *batch) walkOne(root *location, walkgen uint32, enqueue func(*location)) {
	// 数据流图具有负边（从寻址
	// 操作），所以我们使用Bellman-Ford算法。然而
	// 我们不必担心无限的负循环，因为
	// 我们将中间取消引用计数限制为0。

	root.walkgen = walkgen
	root.derefs = 0
	root.dst = nil

	todo := []*location{root} // 后进先出队列
	for len(todo) > 0 {
		l := todo[len(todo)-1]
		todo = todo[:len(todo)-1]

		derefs := l.derefs

		// 如果l.derefs<0，则l的地址流向根。
		addressOf := derefs < 0
		if addressOf {
			// 对于像“root=&l；l=x”这样的流路径，
			// l的地址流向根，但x的地址流向根
			// 不我们通过下限来识别这一点
			// 德里夫斯在0。
			derefs = 0

			// 如果l的地址流向非瞬态
			// 地点，那么我不可能是暂时的
			// 分配。
			if !root.transient && l.transient {
				l.transient = false
				enqueue(l)
			}
		}

		if b.outlives(root, l) {
			// l的值流向根。如果l是一个函数
			// 参数和根是堆或
			// 对应的结果参数，然后记录
			// 用于标记函数的值流
			// 后来
			if l.isName(ir.PPARAM) {
				if (logopt.Enabled() || base.Flag.LowerM >= 2) && !l.escapes {
					if base.Flag.LowerM >= 2 {
						fmt.Printf("%s: parameter %v leaks to %s with derefs=%d:\n", base.FmtPos(l.n.Pos()), l.n, b.explainLoc(root), derefs)
					}
					explanation := b.explainPath(root, l)
					if logopt.Enabled() {
						var e_curfn *ir.Func // TODO（mdempsky）：修复。
						logopt.LogOpt(l.n.Pos(), "leak", "escape", ir.FuncName(e_curfn),
							fmt.Sprintf("parameter %v leaks to %s with derefs=%d", l.n, b.explainLoc(root), derefs), explanation)
					}
				}
				l.leakTo(root, derefs)
			}

			// 如果我的地址在某个地方
			// 活过它，那么我就需要成为堆
			// 分配。
			if addressOf && !l.escapes {
				if logopt.Enabled() || base.Flag.LowerM >= 2 {
					if base.Flag.LowerM >= 2 {
						fmt.Printf("%s: %v escapes to heap:\n", base.FmtPos(l.n.Pos()), l.n)
					}
					explanation := b.explainPath(root, l)
					if logopt.Enabled() {
						var e_curfn *ir.Func // TODO（mdempsky）：修复。
						logopt.LogOpt(l.n.Pos(), "escape", "escape", ir.FuncName(e_curfn), fmt.Sprintf("%v escapes to heap", l.n), explanation)
					}
				}
				l.escapes = true
				enqueue(l)
				continue
			}
		}

		for i, edge := range l.edges {
			if edge.src.escapes {
				continue
			}
			d := derefs + edge.derefs
			if edge.src.walkgen != walkgen || edge.src.derefs > d {
				edge.src.walkgen = walkgen
				edge.src.derefs = d
				edge.src.dst = l
				edge.src.dstEdgeIdx = i
				todo = append(todo, edge.src)
			}
		}
	}
}

// explainPath打印src如何流向walk root的解释。
func (b *batch) explainPath(root, src *location) []*logopt.LoggedOpt {
	visited := make(map[*location]bool)
	pos := base.FmtPos(src.n.Pos())
	var explanation []*logopt.LoggedOpt
	for {
		// 防止无限循环。
		if visited[src] {
			if base.Flag.LowerM >= 2 {
				fmt.Printf("%s:   warning: truncated explanation due to assignment cycle; see golang.org/issue/35518\n", pos)
			}
			break
		}
		visited[src] = true
		dst := src.dst
		edge := &dst.edges[src.dstEdgeIdx]
		if edge.src != src {
			base.Fatalf("path inconsistency: %v != %v", edge.src, src)
		}

		explanation = b.explainFlow(pos, dst, src, edge.derefs, edge.notes, explanation)

		if dst == root {
			break
		}
		src = dst
	}

	return explanation
}

func (b *batch) explainFlow(pos string, dst, srcloc *location, derefs int, notes *note, explanation []*logopt.LoggedOpt) []*logopt.LoggedOpt {
	ops := "&"
	if derefs >= 0 {
		ops = strings.Repeat("*", derefs)
	}
	print := base.Flag.LowerM >= 2

	flow := fmt.Sprintf("   flow: %s = %s%v:", b.explainLoc(dst), ops, b.explainLoc(srcloc))
	if print {
		fmt.Printf("%s:%s\n", pos, flow)
	}
	if logopt.Enabled() {
		var epos src.XPos
		if notes != nil {
			epos = notes.where.Pos()
		} else if srcloc != nil && srcloc.n != nil {
			epos = srcloc.n.Pos()
		}
		var e_curfn *ir.Func // TODO（mdempsky）：修复。
		explanation = append(explanation, logopt.NewLoggedOpt(epos, "escflow", "escape", ir.FuncName(e_curfn), flow))
	}

	for note := notes; note != nil; note = note.next {
		if print {
			fmt.Printf("%s:     from %v (%v) at %s\n", pos, note.where, note.why, base.FmtPos(note.where.Pos()))
		}
		if logopt.Enabled() {
			var e_curfn *ir.Func // TODO（mdempsky）：修复。
			explanation = append(explanation, logopt.NewLoggedOpt(note.where.Pos(), "escflow", "escape", ir.FuncName(e_curfn),
				fmt.Sprintf("     from %v (%v)", note.where, note.why)))
		}
	}
	return explanation
}

func (b *batch) explainLoc(l *location) string {
	if l == &b.heapLoc {
		return "{heap}"
	}
	if l.n == nil {
		// TODO（mdempsky）：完全省略。
		return "{temp}"
	}
	if l.n.Op() == ir.ONAME {
		return fmt.Sprintf("%v", l.n)
	}
	return fmt.Sprintf("{storage for %v}", l.n)
}

// outlives报告存储在l中的值是否可以在
// 如果堆栈已分配，则为其他堆栈的生存期。
func (b *batch) outlives(l, other *location) bool {
	// 这堆东西比任何东西都长寿。
	if l.escapes {
		return true
	}

	// 我们不知道调用方如何处理返回的值，所以
	// 悲观地说，我们需要假设它们流到堆中
	// 也比一切都长寿。
	if l.isName(ir.PPARAMOUT) {
		// 异常：直接调用的闭包可以返回
		// 在不强制的情况下在其外部分配的位置
		// 把它们堆起来。例如：
		// None
		// 变量u int
		// *（func（）*int{return&u}（））=42
		if containsClosure(other.curfn, l.curfn) && l.curfn.ClosureCalled() {
			return false
		}

		return true
	}

	// 如果l和其他在同一个函数中，则l
	// 如果它是在其他循环之外声明的，则比其他循环长
	// 范围例如：
	// None
	// 变量l*int
	// 为了{
	// l=新（整数）
	// }
	if l.curfn == other.curfn && l.loopDepth < other.loopDepth {
		return true
	}

	// 如果在l所在的子闭包中声明了other
	// 声明，那么我就活得更久了。例如：
	// None
	// 变量l*int
	// func（）{
	// l=新（整数）
	// }
	if containsClosure(l.curfn, other.curfn) {
		return true
	}

	return false
}

// containsClosure报告c是否是包含在f中的闭包。
func containsClosure(f, c *ir.Func) bool {
	// 常见情况。
	if f == c {
		return false
	}

	// 函数Foo中的闭包命名为“Foo.funcN…”
	// TODO（mdempsky）：更好地认识这一点。
	fn := f.Sym().Name
	cn := c.Sym().Name
	return len(cn) > len(fn) && cn[:len(fn)] == fn && cn[len(fn)] == '.'
}

// 泄漏记录参数l泄漏到接收器。
func (l *location) leakTo(sink *location, derefs int) {
	// 如果sink是不转义的结果参数（#44614）
	// 我们可以将返回位放入转义分析标签中，
	// 然后记录泄漏的结果。
	if !sink.escapes && sink.isName(ir.PPARAMOUT) && sink.curfn == l.curfn {
		ri := sink.resultIndex - 1
		if ri < numEscResults {
			// 泄漏到结果参数。
			l.paramEsc.AddResult(ri, derefs)
			return
		}
	}

	// 否则，记录为堆泄漏。
	l.paramEsc.AddHeap(derefs)
}

func (b *batch) finish(fns []*ir.Func) {
	// 记录包导出数据的参数标记。
	for _, fn := range fns {
		fn.SetEsc(escFuncTagged)

		narg := 0
		for _, fs := range &types.RecvsParams {
			for _, f := range fs(fn.Type()).Fields().Slice() {
				narg++
				f.Note = b.paramTag(fn, narg, f)
			}
		}
	}

	for _, loc := range b.allLocs {
		n := loc.n
		if n == nil {
			continue
		}
		if n.Op() == ir.ONAME {
			n := n.(*ir.Name)
			n.Opt = nil
		}

		// 根据逃逸分析结果更新n.Esc。

		if loc.escapes {
			if n.Op() == ir.ONAME {
				if base.Flag.CompilingRuntime {
					base.ErrorfAt(n.Pos(), "%v escapes to heap, not allowed in runtime", n)
				}
				if base.Flag.LowerM != 0 {
					base.WarnfAt(n.Pos(), "moved to heap: %v", n)
				}
			} else {
				if base.Flag.LowerM != 0 {
					base.WarnfAt(n.Pos(), "%v escapes to heap", n)
				}
				if logopt.Enabled() {
					var e_curfn *ir.Func // TODO（mdempsky）：修复。
					logopt.LogOpt(n.Pos(), "escape", "escape", ir.FuncName(e_curfn))
				}
			}
			n.SetEsc(ir.EscHeap)
		} else {
			if base.Flag.LowerM != 0 && n.Op() != ir.ONAME {
				base.WarnfAt(n.Pos(), "%v does not escape", n)
			}
			n.SetEsc(ir.EscNone)
			if loc.transient {
				switch n.Op() {
				case ir.OCLOSURE:
					n := n.(*ir.ClosureExpr)
					n.SetTransient(true)
				case ir.OCALLPART:
					n := n.(*ir.SelectorExpr)
					n.SetTransient(true)
				case ir.OSLICELIT:
					n := n.(*ir.CompLitExpr)
					n.SetTransient(true)
				}
			}
		}
	}
}

func (l *location) isName(c ir.Class) bool {
	return l.n != nil && l.n.Op() == ir.ONAME && l.n.(*ir.Name).Class == c
}

const numEscResults = 7

// 泄漏表示来自参数的一组赋值流
// 堆或其任何函数（第一个numEscResults）
// 结果参数。
type leaks [1 + numEscResults]uint8

// Empty报告l是否为空集（即，没有分配流）。
func (l leaks) Empty() bool { return l == leaks{} }

// Heap返回l中任何赋值流的最小deref计数
// 到堆里去。如果不存在这样的流，堆将返回-1。
func (l leaks) Heap() int { return l.get(0) }

// 结果返回来自的任何分配流的最小deref计数
// l到其函数的第i个结果参数。如果不存在这种流动，
// 结果返回-1。
func (l leaks) Result(i int) int { return l.get(1 + i) }

// AddHeap将分配流从l添加到堆。
func (l *leaks) AddHeap(derefs int) { l.add(0, derefs) }

// AddResult将赋值流从l添加到其函数的i'th
// 结果参数。
func (l *leaks) AddResult(i, derefs int) { l.add(1+i, derefs) }

func (l *leaks) setResult(i, derefs int) { l.set(1+i, derefs) }

func (l leaks) get(i int) int { return int(l[i]) - 1 }

func (l *leaks) add(i, derefs int) {
	if old := l.get(i); old < 0 || derefs < old {
		l.set(i, derefs)
	}
}

func (l *leaks) set(i, derefs int) {
	v := derefs + 1
	if v < 0 {
		base.Fatalf("invalid derefs count: %v", derefs)
	}
	if v > math.MaxUint8 {
		v = math.MaxUint8
	}

	l[i] = uint8(v)
}

// “优化”将删除长度或长度相等的结果流路径
// 比最短堆流路径长。
func (l *leaks) Optimize() {
	// 如果我们有一个到堆的路径，那么
	// 在其他地方保持相等或更长的路径。
	if x := l.Heap(); x >= 0 {
		for i := 0; i < numEscResults; i++ {
			if l.Result(i) >= x {
				l.setResult(i, -1)
			}
		}
	}
}

var leakTagCache = map[leaks]string{}

// Encode将l转换为用于导出数据的二进制字符串。
func (l leaks) Encode() string {
	if l.Heap() == 0 {
		// 空间优化：空字符串编码更多
		// 高效地导出数据。
		return ""
	}
	if s, ok := leakTagCache[l]; ok {
		return s
	}

	n := len(l)
	for n > 0 && l[n-1] == 0 {
		n--
	}
	s := "esc:" + string(l[:n])
	leakTagCache[l] = s
	return s
}

// parseLeaks解析表示泄漏的二进制字符串
func parseLeaks(s string) leaks {
	var l leaks
	if !strings.HasPrefix(s, "esc:") {
		l.AddHeap(0)
		return l
	}
	copy(l[:], s[4:])
	return l
}

func Funcs(all []ir.Node) {
	ir.VisitFuncsBottomUp(all, Batch)
}

const (
	escFuncUnknown = 0 + iota
	escFuncPlanned
	escFuncStarted
	escFuncTagged
)

// 将没有回跳的标签标记为不增加e.loopdepth。
type labelState int

const (
	looping labelState = 1 + iota
	nonlooping
)

func isSliceSelfAssign(dst, src ir.Node) bool {
	// 检测以下特殊情况。
	// None
	// func（b*缓冲区）Foo（）{
	// n、 m:=。。。
	// b、 buf=b.buf[n:m]
	// }
	// None
	// 这项作业不适用于逃逸分析，
	// 它不会将任何尚未存在的新指针存储到b中。
	// 但是，如果没有这种特殊情况，b将逃逸，因为我们将分配给OIND/ODOTPTR。
	// 这里我们假设语句不包含调用，
	// 也就是说，该顺序将把所有调用移动到init。
	// 否则，基于名称的值可能会在这两个时刻之间发生变化
	// 当我们评估dst和src时。

	// dst是ONAME解引用。
	var dstX ir.Node
	switch dst.Op() {
	default:
		return false
	case ir.ODEREF:
		dst := dst.(*ir.StarExpr)
		dstX = dst.X
	case ir.ODOTPTR:
		dst := dst.(*ir.SelectorExpr)
		dstX = dst.X
	}
	if dstX.Op() != ir.ONAME {
		return false
	}
	// src是一个切片操作。
	switch src.Op() {
	case ir.OSLICE, ir.OSLICE3, ir.OSLICESTR:
		// 好啊
	case ir.OSLICEARR, ir.OSLICE3ARR:
		// 由于数组嵌入到包含对象中，
		// 非指针数组的切片将向b中引入一个尚未存在的新指针
		// （指向b本身的指针）。在该赋值之后，如果b内容转义，
		// b也逃跑了。如果我们忽略了这样的奥斯陆协议，我们将得出结论
		// 当b内容逃逸时，b不会逃逸。
		// None
		// 指向数组的指针是可以的，因为它不是直接存储在b中的。
		// 对于切片数组（不是指向数组的指针），有一个隐式OADDR。
		// 我们检查它以确定非指针数组切片。
		src := src.(*ir.SliceExpr)
		if src.X.Op() == ir.OADDR {
			return false
		}
	default:
		return false
	}
	// 切片应用于ONAME取消引用。
	var baseX ir.Node
	switch base := src.(*ir.SliceExpr).X; base.Op() {
	default:
		return false
	case ir.ODEREF:
		base := base.(*ir.StarExpr)
		baseX = base.X
	case ir.ODOTPTR:
		base := base.(*ir.SelectorExpr)
		baseX = base.X
	}
	if baseX.Op() != ir.ONAME {
		return false
	}
	// dst和src引用相同的基ONAME。
	return dstX.(*ir.Name) == baseX.(*ir.Name)
}

// isSelfAssign报告是否可以从src分配到dst
// 逃避分析忽略了这一点，因为它实际上是一种自我分配。
func isSelfAssign(dst, src ir.Node) bool {
	if isSliceSelfAssign(dst, src) {
		return true
	}

	// 检测分配回同一对象的琐碎分配。
	// None
	// 它涵盖以下情况：
	// val.x=val.y
	// val.x[i]=val.y[j]
	// val.x1.x2=val.x1.y2
	// ... 等
	// None
	// 这些指定不会更改指定的对象生存期。

	if dst == nil || src == nil || dst.Op() != src.Op() {
		return false
	}

	// 表达式前缀必须为“安全”且相同。
	switch dst.Op() {
	case ir.ODOT, ir.ODOTPTR:
		// 允许不同的安全尾部存取器。
		dst := dst.(*ir.SelectorExpr)
		src := src.(*ir.SelectorExpr)
		return ir.SameSafeExpr(dst.X, src.X)
	case ir.OINDEX:
		dst := dst.(*ir.IndexExpr)
		src := src.(*ir.IndexExpr)
		if mayAffectMemory(dst.Index) || mayAffectMemory(src.Index) {
			return false
		}
		return ir.SameSafeExpr(dst.X, src.X)
	default:
		return false
	}
}

// mayAffectMemory报告n的计算是否会影响程序的
// 内存状态。如果表达式不能影响内存状态，那么它可以
// 安全地被逃逸分析忽略。
func mayAffectMemory(n ir.Node) bool {
	// 我们可能希望使用“内存安全”操作列表，而不是一般的
	// “无副作用”，包括所有可能的呼叫和其他操作
	// 分配或更改全局状态。现在，从后者开始比较安全。
	// None
	// 我们忽略了被零除，索引超出范围，
	// 这里是零指针解引用。

	// TODO（rsc）：看起来应该可以用
	// 一个ir。任何人都在寻找任何不是案例陈述中的op。
	// 但这会在buildall检测到的编译输出中产生更改。
	switch n.Op() {
	case ir.ONAME, ir.OLITERAL, ir.ONIL:
		return false

	case ir.OADD, ir.OSUB, ir.OOR, ir.OXOR, ir.OMUL, ir.OLSH, ir.ORSH, ir.OAND, ir.OANDNOT, ir.ODIV, ir.OMOD:
		n := n.(*ir.BinaryExpr)
		return mayAffectMemory(n.X) || mayAffectMemory(n.Y)

	case ir.OINDEX:
		n := n.(*ir.IndexExpr)
		return mayAffectMemory(n.X) || mayAffectMemory(n.Index)

	case ir.OCONVNOP, ir.OCONV:
		n := n.(*ir.ConvExpr)
		return mayAffectMemory(n.X)

	case ir.OLEN, ir.OCAP, ir.ONOT, ir.OBITNOT, ir.OPLUS, ir.ONEG, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
		n := n.(*ir.UnaryExpr)
		return mayAffectMemory(n.X)

	case ir.ODOT, ir.ODOTPTR:
		n := n.(*ir.SelectorExpr)
		return mayAffectMemory(n.X)

	case ir.ODEREF:
		n := n.(*ir.StarExpr)
		return mayAffectMemory(n.X)

	default:
		return true
	}
}

// HeapAllocReason返回给定节点必须为heap的原因
// 已分配，如果未分配，则为空字符串。
func HeapAllocReason(n ir.Node) string {
	if n == nil || n.Type() == nil {
		return ""
	}

	// 参数始终通过堆栈传递。
	if n.Op() == ir.ONAME {
		n := n.(*ir.Name)
		if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT {
			return ""
		}
	}

	if n.Type().Width > ir.MaxStackVarSize {
		return "too large for stack"
	}

	if (n.Op() == ir.ONEW || n.Op() == ir.OPTRLIT) && n.Type().Elem().Width > ir.MaxImplicitStackVarSize {
		return "too large for stack"
	}

	if n.Op() == ir.OCLOSURE && typecheck.ClosureType(n.(*ir.ClosureExpr)).Size() > ir.MaxImplicitStackVarSize {
		return "too large for stack"
	}
	if n.Op() == ir.OCALLPART && typecheck.PartialCallType(n.(*ir.SelectorExpr)).Size() > ir.MaxImplicitStackVarSize {
		return "too large for stack"
	}

	if n.Op() == ir.OMAKESLICE {
		n := n.(*ir.MakeExpr)
		r := n.Cap
		if r == nil {
			r = n.Len
		}
		if !ir.IsSmallIntConst(r) {
			return "non-constant size"
		}
		if t := n.Type(); t.Elem().Width != 0 && ir.Int64Val(r) > ir.MaxImplicitStackVarSize/t.Elem().Width {
			return "too large for stack"
		}
	}

	return ""
}

// 此特殊标记应用于uintptr变量
// 我们认为这可能是不安全的
// 调用程序集函数。
const UnsafeUintptrNote = "unsafe-uintptr"

// 此特殊标记应用于函数的uintptr参数
// 标记的go:uinttrescapes。
const UintptrEscapesNote = "uintptr-escapes"

func (b *batch) paramTag(fn *ir.Func, narg int, f *types.Field) string {
	name := func() string {
		if f.Sym != nil {
			return f.Sym.Name
		}
		return fmt.Sprintf("arg#%d", narg)
	}

	if len(fn.Body) == 0 {
		// 假设uintptr参数必须在整个调用中保持活动状态。
		// 这对于syscall.syscall来说是最重要的。
		// 见golang.org/issue/13372。
		// 这与逃逸分析本身没有多大关系，
		// 但是我们正在重用注释单个函数的功能
		// 参数，并将这些注释传递给导入代码。
		if f.Type.IsUintptr() {
			if base.Flag.LowerM != 0 {
				base.WarnfAt(f.Pos, "assuming %v is unsafe uintptr", name())
			}
			return UnsafeUintptrNote
		}

		if !f.Type.HasPointers() { // 不要费心为标量添加标签
			return ""
		}

		var esc leaks

		// 外部功能被认为是不安全的，除非
		// None
		if fn.Pragma&ir.Noescape != 0 {
			if base.Flag.LowerM != 0 && f.Sym != nil {
				base.WarnfAt(f.Pos, "%v does not escape", name())
			}
		} else {
			if base.Flag.LowerM != 0 && f.Sym != nil {
				base.WarnfAt(f.Pos, "leaking param: %v", name())
			}
			esc.AddHeap(0)
		}

		return esc.Encode()
	}

	if fn.Pragma&ir.UintptrEscapes != 0 {
		if f.Type.IsUintptr() {
			if base.Flag.LowerM != 0 {
				base.WarnfAt(f.Pos, "marking %v as escaping uintptr", name())
			}
			return UintptrEscapesNote
		}
		if f.IsDDD() && f.Type.Elem().IsUintptr() {
			// 最后一个论点是……uintptr。
			if base.Flag.LowerM != 0 {
				base.WarnfAt(f.Pos, "marking %v as escaping ...uintptr", name())
			}
			return UintptrEscapesNote
		}
	}

	if !f.Type.HasPointers() { // 不要费心为标量添加标签
		return ""
	}

	// 未命名参数未使用，因此不会转义。
	if f.Sym == nil || f.Sym.IsBlank() {
		var esc leaks
		return esc.Encode()
	}

	n := f.Nname.(*ir.Name)
	loc := b.oldLoc(n)
	esc := loc.paramEsc
	esc.Optimize()

	if base.Flag.LowerM != 0 && !loc.escapes {
		if esc.Empty() {
			base.WarnfAt(f.Pos, "%v does not escape", name())
		}
		if x := esc.Heap(); x >= 0 {
			if x == 0 {
				base.WarnfAt(f.Pos, "leaking param: %v", name())
			} else {
				// TODO（mdempsky）：像下面这样提到level=x？
				base.WarnfAt(f.Pos, "leaking param content: %v", name())
			}
		}
		for i := 0; i < numEscResults; i++ {
			if x := esc.Result(i); x >= 0 {
				res := fn.Type().Results().Field(i).Sym
				base.WarnfAt(f.Pos, "leaking param: %v to result %v level=%d", name(), res, x)
			}
		}
	}

	return esc.Encode()
}
